﻿using System.Collections.Generic;
using Expect.Core.Interfaces;
using Expect.Core.Helpers;
namespace Expect.Core.BaseClasses
{
    using System.Linq;
    using System;

    /// <summary>
    /// Default implementation for execution context
    /// </summary>
    public class DefaultExecutionContext : IExecutionContext
    {
        /// <summary>
        /// Indicate if the context must only resolve specifications and consequently not execute them
        /// </summary>
        public bool ResolveOnly { get; set; }

        private object container;

        /// <summary>
        /// execution container
        /// </summary>
        public object Container
        {
            get
            {
                return this.container;
            }
            set
            {
                this.container = value;
                if (value != null)
                {
                    Catalog.ImportTypes(value.GetType().Assembly);
                    var elt = value as ISpecificationImplementation;
                    if (elt != null)
                    {
                        elt.Context = this;
                    }
                }
            }
        }

        private Dictionary<string, object> data = new Dictionary<string, object>();

        /// <summary>
        /// Context data
        /// </summary>
        public Dictionary<string, object> Data
        {
            get
            {
                return this.data;
            }
        }

        /// <summary>
        /// Catalog of specs and fixtures implementations
        /// </summary>
        public IExpectCatalog Catalog { get; set; }

        private ExecutionSummary _summary = new ExecutionSummary();

        /// <summary>
        /// Summary of the execution result
        /// </summary>
        public ExecutionSummary Summary
        {
            get { return _summary; }
            set { _summary = value; }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public DefaultExecutionContext()
        {
            
            NameProvider = new DefaultNameProvider();
            Catalog = new DefaultFixtureCatalog(NameProvider);
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="catalog"></param>
        /// <param name="provider"></param>
        public DefaultExecutionContext(IExpectCatalog catalog, INameProvider provider)
        {
            //SummarizeErrors = true;
            NameProvider = provider;
            Catalog = catalog;
        }

        /// <summary>
        /// Name provider for the resolution of the names
        /// </summary>
        public INameProvider NameProvider { get; set; }

        /// <summary>
        /// Get a setup specification from named specification container
        /// </summary>
        /// <param name="spec"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public SpecificationDefinition GetSetupFrom(SpecificationDefinition spec, string name)
        {
            return GetSpecMethod<SetupProviderAttribute>(spec, name);
        }

        private SpecificationDefinition GetSpecMethod<T>(SpecificationDefinition spec, string name) where T : Attribute
        {
            SpecificationDefinition res;
            var container = this.Catalog.GetContainerFor(name);
            var containerbackup = this.Container;

            this.Container = container;
            try
            {
                container.Context = this;
                res = container.ApplySpecificationFor<T>(spec);
            }
            finally
            {
                this.Container = containerbackup;
            }
            return res;
        }

        /// <summary>
        /// Get a teardown specification from named specification container
        /// </summary>
        /// <param name="spec"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public SpecificationDefinition GetTeardownFrom(SpecificationDefinition spec, string name)
        {
            return GetSpecMethod<TeardownProviderAttribute>(spec, name);
        }

        /// <summary>
        /// Indexer on data
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public object this[string name]
        {
            get
            {
                return Data[name];
            }
            set
            {
                Data[name] = value;
            }
        }

        /// <summary>
        /// Indicate if context data contains this element
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool HasDataFor(string name)
        {
            return Data.ContainsKey(name);
        }
    }
}
